package com.focus.pay.wx.action.v3;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.http.Method;
import cn.hutool.json.JSONUtil;
import com.focus.pay.wx.config.WxPayConfig;
import com.focus.pay.wx.constant.WxPayConstant;
import com.focus.pay.core.exception.PayException;
import com.focus.pay.core.pay.*;
import com.focus.pay.core.utils.HttpClientUtils;
import com.focus.pay.wx.model.cancel.v3.WxPayModelCancel;
import com.focus.pay.wx.model.order.v2.WxPayModelQueryResponse;
import com.focus.pay.wx.model.order.v3.WxPayCommon;
import com.focus.pay.wx.model.order.v3.WxPayModelQueryByOutTradeNo;
import com.focus.pay.wx.model.order.v3.WxPayModelQueryByTransaction;
import com.focus.pay.wx.model.download.v3.WxPayModelDownloadResponse;
import com.focus.pay.wx.model.download.v3.WxPayModelFundFlowBill;
import com.focus.pay.wx.model.download.v3.WxPayModelTradeBill;
import com.focus.pay.wx.model.refund.v3.WxPayModelRefund;
import com.focus.pay.wx.model.refund.v3.WxPayModelRefundQuery;
import com.focus.pay.wx.model.refund.v3.WxPayModelRefundQueryResponse;
import com.focus.pay.wx.model.refund.v3.WxPayModelRefundResponse;
import lombok.Data;

import java.util.Map;

/**
 * @Author: 赵参谋
 * @Date: 2021/10/12 11:25
 * @Description: 微信支付V3
 * 1 微信支付结果查询: 支付订单号查询/商户订单号查询
 * 2 微信支付撤销交易
 * 3 微信支付申请退款
 * 4 微信支付退款查询
 * 5 微信申请交易账单
 * 6 微信申请资金账单
 * 7 微信账单下载
 */
public class WxPay implements
        PayQueryInterface<WxPayModelQueryResponse, WxPayModelQueryByTransaction, WxPayConfig>,
        PayCloseInterface<Boolean, WxPayModelCancel,WxPayConfig>,
        PayRefundInterface<com.focus.pay.wx.model.refund.v3.WxPayModelRefundResponse, com.focus.pay.wx.model.refund.v3.WxPayModelRefund,WxPayConfig>,
        PayRefundQueryInterface<com.focus.pay.wx.model.refund.v3.WxPayModelRefundQueryResponse, com.focus.pay.wx.model.refund.v3.WxPayModelRefundQuery,WxPayConfig>,
        PayTradeDownlaodInterface<com.focus.pay.wx.model.download.v3.WxPayModelDownloadResponse, com.focus.pay.wx.model.download.v3.WxPayModelTradeBill,WxPayConfig> {

    /**
     * 微信支付订单号查询
     * @param wxPayModelQueryByTransaction 订单查询参数
     * @param wxPayConfig 配置参数
     * @return 查询结果
     * @throws PayException
     */
    @Override
    public WxPayModelQueryResponse payQuery(WxPayModelQueryByTransaction wxPayModelQueryByTransaction, WxPayConfig wxPayConfig) throws PayException {
        DeployParams deployParams = instanceDeployParams(WxPayConstant.WxPayPathV3.QUERY_ORDER_TRANSACTION,
                new String[]{wxPayModelQueryByTransaction.getTransaction_id(),wxPayConfig.getMchId()});
        String res = HttpClientUtils.doGet(
                buildRequestPath(deployParams),
                buildHead(Method.GET,wxPayModelQueryByTransaction,wxPayConfig,deployParams)
        );
        return resolvingResult(res,WxPayModelQueryResponse.class);
    }

    /**
     * 微信商户单号查询
     * @param wxPayModelQueryByOutTradeNo 订单查询参数
     * @param wxPayConfig 配置参数
     * @return 查询结果
     * @throws PayException
     */
    public WxPayModelQueryResponse payQuery(WxPayModelQueryByOutTradeNo wxPayModelQueryByOutTradeNo, WxPayConfig wxPayConfig) throws PayException {
        DeployParams deployParams = instanceDeployParams(WxPayConstant.WxPayPathV3.QUERY_ORDER_OUTTRADENO,
                new String[]{wxPayModelQueryByOutTradeNo.getOut_trade_no(),wxPayConfig.getMchId()});
        String res = HttpClientUtils.doGet(
                buildRequestPath(deployParams),
                buildHead(Method.GET,wxPayModelQueryByOutTradeNo,wxPayConfig,deployParams));
        return resolvingResult(res,WxPayModelQueryResponse.class);
    }

    /**
     * 关闭订单
     * @param wxPayModelCancel 关闭订单参数
     * @param wxPayConfig 配置参数
     * @return 是否成功
     */
    @Override
    public Boolean payClose(WxPayModelCancel wxPayModelCancel, WxPayConfig wxPayConfig) throws PayException {
        DeployParams deployParams = instanceDeployParams(WxPayConstant.WxPayPathV3.CANCEL_ORDER,
                new String[]{wxPayModelCancel.getOut_trade_no()});
        HttpClientUtils.doPost(
                buildRequestPath(deployParams),
                buildBody(wxPayModelCancel,wxPayConfig),
                buildHead(Method.POST,wxPayModelCancel,wxPayConfig,deployParams)
        );
        return true;
    }

    /**
     * 申请退款
     * @param wxPayModelRefund 退款申请参数
     * @param wxPayConfig 配置参数
     * @return 退款结果
     */
    @Override
    public com.focus.pay.wx.model.refund.v3.WxPayModelRefundResponse payRefund(WxPayModelRefund wxPayModelRefund, WxPayConfig wxPayConfig) throws PayException {
        DeployParams deployParams = instanceDeployParams(WxPayConstant.WxPayPathV3.REFUND_ORDER,null);
        String res = HttpClientUtils.doPost(
                buildRequestPath(deployParams),
                buildBody(wxPayModelRefund,wxPayConfig),
                buildHead(Method.POST,wxPayModelRefund,wxPayConfig,deployParams));
        return resolvingResult(res, WxPayModelRefundResponse.class);
    }

    /**
     * 微信退款查询
     * @param wxPayModelRefundQuery 退款查询参数
     * @param wxPayConfig 配置参数
     * @return 查询结果
     */
    @Override
    public com.focus.pay.wx.model.refund.v3.WxPayModelRefundQueryResponse payRefundQuery(WxPayModelRefundQuery wxPayModelRefundQuery, WxPayConfig wxPayConfig) throws PayException {
        DeployParams deployParams = instanceDeployParams(WxPayConstant.WxPayPathV3.REFUND_QUERY
                ,new String[]{wxPayModelRefundQuery.getOut_refund_no()});
        String res = HttpClientUtils.doGet(
                buildRequestPath(deployParams),
                buildHead(Method.GET,wxPayModelRefundQuery,wxPayConfig,deployParams)
        );
        return resolvingResult(res, WxPayModelRefundQueryResponse.class);
    }

    /**
     * 申请交易账单
     * @param wxPayModelTradeBill
     * @param wxPayConfig
     * @return 下载地址
     */
    @Override
    public com.focus.pay.wx.model.download.v3.WxPayModelDownloadResponse tradeDownload(WxPayModelTradeBill wxPayModelTradeBill, WxPayConfig wxPayConfig) throws PayException {
        DeployParams deployParams = instanceDeployParams(WxPayConstant.WxPayPathV3.TRADE_BILL,
                new String[]{wxPayModelTradeBill.getBill_date(),wxPayModelTradeBill.getBill_type()});
        String res = HttpClientUtils.doGet(
                buildRequestPath(deployParams),
                buildHead(Method.GET,wxPayModelTradeBill,wxPayConfig,deployParams));
        return resolvingResult(res, com.focus.pay.wx.model.download.v3.WxPayModelDownloadResponse.class);
    }

    /**
     * 申请资金账单
     * @param wxPayModelFundFlowBill 资金账单下载参数
     * @param wxPayConfig 配置参数
     * @return 下载地址
     */
    public com.focus.pay.wx.model.download.v3.WxPayModelDownloadResponse tradeDownload(WxPayModelFundFlowBill wxPayModelFundFlowBill, WxPayConfig wxPayConfig) throws PayException {
        DeployParams deployParams = instanceDeployParams(WxPayConstant.WxPayPathV3.FUNDFLOW_BILL,
                new String[]{wxPayModelFundFlowBill.getBill_date(),wxPayModelFundFlowBill.getAccount_type()});
        String res = HttpClientUtils.doGet(
                buildRequestPath(deployParams),
                buildHead(Method.GET,wxPayModelFundFlowBill,wxPayConfig,deployParams));
        return resolvingResult(res, WxPayModelDownloadResponse.class);
    }

    /**
     * 下载账单
     * @param downloadUrl 下载地址
     */
    public byte[] tradeDownload(String downloadUrl,WxPayConfig wxPayConfig) throws PayException {
        DeployParams deployParams = instanceDeployParams(WxPayConstant.WxPayPathV3.TRADE_DOWNLOAD,
                new String[]{downloadUrl.substring(downloadUrl.indexOf(WxPayConstant.WxPayDomain.API_DOMAIN_V1.getDomain()))});
        return HttpClientUtils.download(
                buildRequestPath(deployParams),
                buildHead(Method.GET,null,wxPayConfig,deployParams));
    }



    /**
     * 获取请求路径
     * @return 请求路径
     */
    protected String buildRequestPath(DeployParams deployParams ){
        return WxPayConstant.WxPayPathV3.buildRequestPath(deployParams.getWxPayPathV3(),deployParams.getParams());
    }

    /**
     * 获取请求绝对URL,去除域名
     * @return 请求绝对UR
     */
    protected String buildRequestRoute(DeployParams deployParams){
        return WxPayConstant.WxPayPathV3.buildRequestRoute(deployParams.getWxPayPathV3(),deployParams.getParams());
    }

    /**
     * 构建请求体
     */
    protected <T extends WxPayCommon> String buildBody(T t, WxPayConfig wxPayConfig){
        t.buildParam(t,wxPayConfig);
        return JSONUtil.toJsonStr(t);
    }

    /**
     * String 转 BEAN
     */
    protected <T> T resolvingResult(String result,Class<T> t){
        return BeanUtil.toBean(result,t,CopyOptions.create());
    }

    /**
     * 构建请求头
     */
    protected <T extends WxPayCommon> Map buildHead(Method method, T t, WxPayConfig wxPayConfig,DeployParams deployParams) throws PayException {
        String route = buildRequestRoute(deployParams);
        return t.buildRequestHead(method,route,t,wxPayConfig);
    }

    public DeployParams instanceDeployParams(WxPayConstant.WxPayPathV3 wxPayPathV3, String[] params) {
        return new DeployParams(wxPayPathV3,params);
    }

    @Data
    public class DeployParams{
        private WxPayConstant.WxPayPathV3 wxPayPathV3;
        private String[] params;

        public DeployParams(WxPayConstant.WxPayPathV3 wxPayPathV3, String[] params) {
            this.wxPayPathV3=wxPayPathV3;
            this.params=params;
        }
    }



}
